package xyz.scootaloo.kami.server.service

import io.vertx.core.Future
import xyz.scootaloo.kami.server.service.impl.InternalCacheServiceImpl

/**
 * 缓存服务
 *
 * 支持以键值对的形式存储各种java类型, 并且可以指定一个过期时间(默认有效期5秒后过期),
 * 在过期时间后, 该键值对会被从缓存中删除, (当过期时间为负数时, 该键值对会在缓存中长期存在)
 *
 * ```kotlin
 * // 当前时间的4秒后, 这个键值对会被删除
 * cacheService.putString("abc", "123", 4000)
 * // 在被删除之前调用, 都会返回"123"
 * cacheService.getString("abc").await()
 * ```
 *
 * @author flutterdash@qq.com
 * @since 2022/2/2 0:27
 */
interface CacheService {

    companion object {
        const val defExpireTime = 5000L

        operator fun invoke(): CacheService {
            return InternalCacheServiceImpl
        }
    }

    fun putByte(key: String, value: Byte, expiry: Long = defExpireTime): Future<Unit>
    fun putShort(key: String, value: Short, expiry: Long = defExpireTime): Future<Unit>
    fun putInteger(key: String, value: Int, expiry: Long = defExpireTime): Future<Unit>
    fun putLong(key: String, value: Long, expiry: Long = defExpireTime): Future<Unit>
    fun putDouble(key: String, value: Double, expiry: Long = defExpireTime): Future<Unit>
    fun putString(key: String, value: String, expiry: Long = defExpireTime): Future<Unit>
    fun <T : Any> putObject(key: String, value: T, expiry: Long = defExpireTime): Future<Unit>

    /**
     * 获取一个键的值, 如果此键存在, 则返回这个存在的值,
     * 当值不存在时, 会通过[lazy]生成一个新值, 并插入以[key]为键的键值对;
     *
     * 如果[expiry]不为负数, 则将键的过期时间修改为新的指定的过期时间, 否则不对键的过期时间进行修改(默认不修改),
     * 如果存入新键值对, 但没有指定[expiry], 则此键值对过期时间被设定为5000
     *
     * @see access
     * @see CacheAccessor
     */
    fun <T> getOrElse(key: String, expiry: Long = -1, lazy: (CacheAccessor) -> T): Future<T>

    fun getByte(key: String): Future<Byte?>
    fun getShort(key: String): Future<Short?>
    fun getInteger(key: String): Future<Int?>
    fun getLong(key: String): Future<Long?>
    fun getDouble(key: String): Future<Double?>
    fun getString(key: String): Future<String?>
    fun <T> get(key: String): Future<T?>

    fun contains(key: String): Future<Boolean>

    fun remove(key: String): Future<Unit>

    /**
     * 提供一个访问器[CacheAccessor], 允许调用者在缓存服务的内部线程安全的执行一些代码,
     * 然后返回代码的执行结果
     */
    fun <T> access(block: (CacheAccessor) -> T): Future<T>

    /**
     * 更新键的过期时间
     *
     * 如果此时键还存在于缓存中, 则更新, 否则不执行任何操作
     *
     * @param key 要更新的键
     * @param newExpiryTime 指定一个新的过期时间
     */
    fun updateExpiryTime(key: String, newExpiryTime: Long): Future<Unit>

    interface CacheAccessor {
        fun contains(key: String): Boolean
        fun <T> get(key: String): T
        fun set(key: String, value: Any, expiry: Long = defExpireTime)
        fun remove(key: String)
        fun updateExpiryTime(key: String, newExpiryTime: Long)
    }

    data class CacheItem(
        val value: Any,
        var expiryTime: Long
    )

    interface KeyStore {
        fun isEmpty(): Boolean
        fun store(newKey: String)
        fun keys(): List<String>
        fun remove(key: String)
    }
}